/*
 * This source code is part of TWaver 4.0
 *
 * Serva Software PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * Copyright 2002 - 2011 Serva Software. All rights reserved.
 */

package demo.network.miscellaneous.game;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;

import javax.swing.JComponent;
import javax.swing.SwingUtilities;

import twaver.TDataBox;
import twaver.TWaverConst;
import twaver.TWaverUtil;
import twaver.network.TNetwork;
import twaver.network.background.ColorBackground;
import demo.DemoPane;

public class GreedySnakeDemo extends DemoPane implements Runnable{

	public final static int WIDTH = 23;
	public final static int MAXX = 20;
	public final static int MAXY = 20;

    public final static int UP = 2;
    public final static int DOWN = 4;
    public final static int LEFT = 1;
    public final static int RIGHT = 3;

    private LinkedList nodeList = new LinkedList();
    private NodePoint food = null;
    private int direction = UP;
    private boolean isRunning = false;
    private boolean isPause = false;
    private int interval = 150;

    private ScoreNode scoreNode = null;
	private TDataBox box = new TDataBox();
	private TNetwork network = new TNetwork(box);

	private int shapeIndex = 0;
	private int gradientIndex = 0;

	private int[] SHAPES = new int[]{
			TWaverConst.SHAPE_CIRCLE,
			TWaverConst.SHAPE_DIAMOND,
			TWaverConst.SHAPE_ROUND_RECTANGLE_QUARTER,
			TWaverConst.SHAPE_STAR,
			TWaverConst.SHAPE_TRIANGLE,
	};

	private int[] GRADIENTS = new int[]{
			TWaverConst.GRADIENT_LINE_NW,
			TWaverConst.GRADIENT_LINE_N,
			TWaverConst.GRADIENT_LINE_NE,
			TWaverConst.GRADIENT_LINE_E,
			TWaverConst.GRADIENT_LINE_SE,
			TWaverConst.GRADIENT_LINE_S,
			TWaverConst.GRADIENT_LINE_SW,
			TWaverConst.GRADIENT_LINE_W,
			TWaverConst.GRADIENT_RADIAL_NW,
			TWaverConst.GRADIENT_RADIAL_N,
			TWaverConst.GRADIENT_RADIAL_NE,
			TWaverConst.GRADIENT_RADIAL_E,
			TWaverConst.GRADIENT_RADIAL_SE,
			TWaverConst.GRADIENT_RADIAL_S,
			TWaverConst.GRADIENT_RADIAL_SW,
			TWaverConst.GRADIENT_RADIAL_W,
			TWaverConst.GRADIENT_RADIAL_C,
	};

	public GreedySnakeDemo(){
		
		ColorBackground background = new ColorBackground(new Color(0, 0, 200));
		background.setGradientColor(new Color(0, 0, 0));
		background.setGradient(true);
		network.setBackground(background);
		this.setBackground(new Color(0, 100, 200));

		JComponent canvas = network.getCanvas();
		network.setEnableAutoAdjustCanvasSize(false);
		network.setEnableSwitchFullScreenWithKeyboard(false);
		
		Dimension size = new Dimension(MAXX * WIDTH, MAXY * WIDTH);
		canvas.setPreferredSize(size);
		canvas.setMaximumSize(size);
		canvas.setMinimumSize(size);

		this.setLayout(new FlowLayout());
		this.add(canvas);

		canvas.addKeyListener(new KeyAdapter(){
            public void keyPressed(KeyEvent e) {
                keyPressedProcess(e);
            }
        });
		new Thread(this).start();

		this.startGame();
	}

    public void run() {
        while (!Thread.interrupted()) {
            try {
                Thread.sleep(interval);
                if(network.getCanvas().isShowing()){
	                SwingUtilities.invokeLater(new Runnable() {
	                    public void run() {
	                    	if (isRunning && !isPause) {
	                    		isRunning = moveNext();
	                    	}
	                    }
	                });                	
                }
            } catch (Exception e) {
            	return;
            }
        }
    }

	private boolean touchNodes(int px, int py){
		for(int i=0; i<this.nodeList.size(); i++){
			NodePoint node = (NodePoint)this.nodeList.get(i);
			if(node.getPx() == px && node.getPy() == py){
				return true;
			}
		}
		return false;
	}

	private boolean touchFood(int px, int py){
		return food != null && food.getPx() == px && food.getPy() == py;
	}

    private boolean moveNext() {
    	NodePoint node = (NodePoint)this.nodeList.getFirst();
    	int nextX = node.getPx();
    	int nextY = node.getPy();

        switch (direction) {
        case UP:
        	nextY--;
            break;
        case DOWN:
        	nextY++;
            break;
        case LEFT:
        	nextX--;
            break;
        case RIGHT:
        	nextX++;
            break;
        }

        if (nextX < 0 || nextX >= MAXX || nextY < 0 || nextY >= MAXY) {
            return false;
        }

        if (touchNodes(nextX, nextY)) {
            return false;
        }

        if (touchFood(nextX, nextY)) {
            this.scoreNode.increase();
            this.nodeList.addFirst(createNode(nextX, nextY));
            this.createNewFoodNode();
        }else{
            this.nodeList.addFirst(createNode(nextX, nextY));
            NodePoint lastElement = (NodePoint)this.nodeList.removeLast();
            box.removeElement(lastElement);
        }

        if(++this.gradientIndex >= GRADIENTS.length){
        	this.gradientIndex = 0;
        }
    	this.food.putCustomDrawGradientFactory(GRADIENTS[gradientIndex]);
    	this.scoreNode.putCustomDrawGradientFactory(GRADIENTS[gradientIndex]);

    	float count = this.nodeList.size();
    	Color color = TWaverUtil.getRandomColor();
    	for(int i=0; i<count; i++){
    		node = (NodePoint)this.nodeList.get(i);
    		node.putCustomDrawFillColor(color);
    		float alpha = (count - i)/(count*2) + 0.45f;
    		node.putRenderAlpha(alpha);
    	}

        return true;
    }

    private NodePoint createNode(int px, int py){
    	NodePoint node = new NodePoint(px, py);
    	node.putCustomDraw(true);
    	node.putCustomDrawAntialias(true);
    	node.putCustomDrawFill(true);
    	node.putCustomDrawFillColor(Color.RED);
    	node.putCustomDrawGradient(true);
    	node.putCustomDrawGradientColor(Color.WHITE);
    	node.putCustomDrawGradientFactory(TWaverConst.GRADIENT_RADIAL_NW);
    	node.putCustomDrawOutline(false);
    	node.putCustomDrawShapeFactory(SHAPES[shapeIndex]);
    	node.putCustomDrawDefaultBorder(false);
    	box.addElement(node);
    	return node;
    }

    private void createNewFoodNode() {
        int px;
        int py;
        do {
            px = TWaverUtil.getRandomInt(MAXX);
            py = TWaverUtil.getRandomInt(MAXY);
        } while (touchFood(px, py) || touchNodes(px, py));

        if(this.food != null){
        	box.removeElement(food);
        }

        if(++this.shapeIndex >= SHAPES.length){
        	this.shapeIndex = 0;
        }

        this.food = createNode(px, py);
        this.food.putCustomDrawFillColor(Color.RED);
        this.food.putCustomDrawGradientColor(Color.WHITE);
        this.food.putCustomDrawOutline(true);
        this.food.putCustomDrawOutlineColor(Color.YELLOW);

    	for(int i=0; i<this.nodeList.size(); i++){
    		NodePoint node = (NodePoint)this.nodeList.get(i);
    		node.putCustomDrawShapeFactory(SHAPES[shapeIndex]);
    	}
    }

    public void startGame(){
    	box.clear();
        this.nodeList.clear();

        int initLength = MAXX;
        for (int i = 0; i < initLength; ++i) {
            int x = MAXX / 2 + i;
            int y = MAXY / 2;
            this.nodeList.add(createNode(x, y));
        }
        this.createNewFoodNode();

        this.direction = UP;
        this.isPause = false;
        this.isRunning = true;

        scoreNode = new ScoreNode();
        box.addElement(scoreNode);
    }

    public void changeDirection(int newDirection) {
        if (direction % 2 != newDirection % 2) {
            direction = newDirection;
        }
    }

    public void keyPressedProcess(KeyEvent e) {
        int keyCode = e.getKeyCode();

        if (this.isRunning && !this.isPause) {
            switch (keyCode) {
            case KeyEvent.VK_UP:
                this.changeDirection(UP);
                break;
            case KeyEvent.VK_DOWN:
                this.changeDirection(DOWN);
                break;
            case KeyEvent.VK_LEFT:
                this.changeDirection(LEFT);
                break;
            case KeyEvent.VK_RIGHT:
                this.changeDirection(RIGHT);
                break;
            }
        }

        if(this.isRunning && keyCode == KeyEvent.VK_SPACE){
            this.isPause = !this.isPause;
            return;
        }
        
        if(!this.isRunning){
            this.startGame();
        }

        
    }

}